// /lib/wapp/wapp.js
// by
// Todd Tanner

// Function bind code
if (Function.prototype.bind == null) {
    Function.prototype.bind = (function (slice){
        // (C) WebReflection - Mit Style License
        function bind(context) {
            var self = this; // "trapped" function reference
            // only if there is more than an argument
            // we are interested into more complex operations
            // this will speed up common bind creation
            // avoiding useless slices over arguments
            if (1 < arguments.length) {
                // extra arguments to send by default
                var $arguments = slice.call(arguments, 1);
                return function () {
                    return self.apply(
                        context,
                        // thanks @kangax for this suggestion
                        arguments.length ?
                            // concat arguments with those received
                            $arguments.concat(slice.call(arguments)) :
                            // send just arguments, no concat, no slice
                            $arguments
                    );
                };
            }
            // optimized callback
            return function () {
                // speed up when function is called without arguments
                return arguments.length ? self.apply(context, arguments) : self.call(context);
            };
        }
        // the named function
        return bind;
    }(Array.prototype.slice));
}
// End Function bind code

function randomString(string_length) {
	var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
	if (!string_length) var string_length = 8;
	var randomstring = '';
	for (var i=0; i<string_length; i++) {
		var rnum = Math.floor(Math.random() * chars.length);
		randomstring += chars.substring(rnum,rnum+1);
	}
	return randomstring;
}


function MenuCtl(){
	this.items = new Array();
}
MenuCtl.prototype = {
	addItem: function(link,title,icon,id,target,detail1,detail2,detail3,detail4,detail5){
		if (!title) return false;
		if (!id) var id = '';
		if (!icon) var icon = '';
		if (!target) var target = '';
		if (!onclick) var onclick = '';
		if (!link) var link = '';
		if (!detail1) var detail1 = '';
		if (!detail2) var detail2 = '';
		if (!detail3) var detail3 = '';
		if (!detail4) var detail4 = '';
		if (!detail5) var detail5 = '';
		var item = {
			id: id,
			link: link,
			onclick: onclick,
			target: target,
			icon: icon,
			title: title,
			detail1: detail1,
			detail2: detail2,
			detail3: detail3,
			detail4: detail4,
			detail5: detail5
		};
		this.items.push(item);
	},
	iconView: function(container_selector){
		$(container_selector).html('');
		var len = this.items.length;
		for(var i=0; i<len; i++) {
			var mItem = this.items[i];
			var htm = '<div';
			if (mItem.id && mItem.id != '')
				htm += ' id="'+mItem.id+'"';
			htm += ' class="menu_item"><a href="'+mItem.link+'"';
			if (mItem.target && mItem.target != '')
				htm += ' target="'+mItem.target+'"';
			htm += '>';
			htm += '<div class="menu_item_img"';
			if (mItem.icon && mItem.icon != '')
				htm += ' style="background-image:url(\''+mItem.icon+'\')"';
			htm += '></div>';
			htm += '<div class="menu_item_title">'+mItem.title+'</div></a></div>';
			$(container_selector).append(htm);
		}			
		$('.menu_item_img').corner();
		//$('.menu_item').show();
	},
	listView: function(container_selector){
		$(container_selector).html('');
		var div = '';
		var len = this.items.length;
		for(var i=0; i<len; i++) {
			var mItem = this.items[i];
			div += '<div class="detailed_row">';
			div += '  <div class="detail detail_toggle">' + mItem.title + '</div>';
			div += '  <div class="detail detail_toggle">' + mItem.detail1 + '</div>';
			div += '  <div class="detail detail_toggle">' + mItem.detail2 + '</div>';
			div += '  <div class="detail more_detail" style="display: none;">' + mItem.detail3 + '</div>';
			div += '  <div class="detail more_detail" style="display: none;">' + mItem.detail4 + '</div>';
			div += '  <div class="detail more_detail" style="display: none;">' + mItem.detail5 + '</div>';					
			div += '  <img class="detail_img detail_toggle" src="' + mItem.icon + '"/>';
			div += '  <div class="all_details_button" onclick="runner.showOrderResult(\''+ mItem.link +'\')"></div>';
			div += '</div>';				
        }
		if ($(container_selector).html() != div) {
			$(container_selector).html(div);
		}
		if (div != ''){
			$('.detail_img').corner();
			$(container_selector).show();
		} else {
			$(container_selector).hide();
		}
	}
};

var client = {
		client_index: -2,
		mobileClients: [
		    "midp",
			"240x320",
			"blackberry",
			"netfront",
			"nokia",
			"panasonic",
			"portalmmm",
			"sharp",
			"sie-",
			"sonyericsson",
			"symbian",
			"windows ce",
			"benq",
			"mda",
			"mot-",
			"opera mini",
			"philips",
			"pocket pc",
			"sagem",
			"samsung",
			"sda",
			"sgh-",
			"vodafone",
			"xda",
			"iphone",
			"android"
		],
	isMobile: function(){
		if (this.client_index == -2) {
			this.client_index = -1;
			userAgent = navigator.userAgent;
			userAgent=userAgent.toLowerCase();
			for (var i in this.mobileClients) {
				if (userAgent.indexOf(this.mobileClients[i]) != -1) {
					this.client_index = i;
				}
			}		
		}
		if (this.client_index == -1) {
			return false;
		} else {
			return true;
		}
	},
	devicetype: function(){
		if (this.client_index == -2) {
			this.isMobile();
		}
		if (this.client_index == -1) {
			return 'pc';
		} else {
			return this.mobileClients[this.client_index];
		}
	}
};

function status_icon() {}
status_icon.prototype = {
	text: ''
};
function status_icon_ctl() {}
status_icon_ctl.prototype = {
	icons: [],
	add: function(icon_id,text,icon_classes,show,onclick){
		if (!this.exists(icon_id)) {
			if (icon_classes && icon_classes != ''){
				icon_classes += ' ';
			} else {
				icon_classes = '';
			}
			icon_classes += 'status_icon';
			var ic = '<div id="' + icon_id + '" class="right_status">';
			ic += '<div class="' + icon_classes + '"></div>';
			ic += '<div class="status_msg">'+text+'</div>';
			ic += '</div>';
			$('#status_bar').append(ic);
			if (onclick) {
				$('#status_bar > #' + icon_id).click(onclick);
			}
			this.icons.push(icon_id);
			if (text && text != '') {
				$('#' + icon_id + '>div').show();
			}
			if (show || typeof show == "undefined") {
				//$('#' + icon_id).show("slide",{},2000);
				$('#' + icon_id).show();
			}
		} else {
			alert('Icon exists.');
		}
	},
	exists: function(icon_id){
		if ($('#' + icon_id).length > 0){
			return true;
		} else {
			return false;
		}
	},
	remove: function(icon_id){
		if (this.exists(icon_id)) {
			var pntr = this.icons.indexOf(icon_id);
			this.icons.splice(pntr,1);
			$('#' + icon_id).remove();
		}
	},
	show: function(icon_id){
		if (this.exists(icon_id)) {
			$('#' + icon_id).show();
		}
	},
	hide: function(icon_id){
		if (this.exists(icon_id)) {
			$('#' + icon_id).hide();
		}
	},
	text: function(icon_id,text){
		if (this.exists(icon_id)) {
			$('#' + icon_id + '> div').html(text);
			if (text && text != '') {
				$('#' + icon_id + '>div').show();
			} else {
				$('#' + icon_id + '>div').hide();
			}		
		}
	}
};

function dbClass(){}
dbClass.prototype = {
    short_name: '',
    version: '',
    display_name: '',
    max_size: 5000000,
	db: null,
	transaction: null,
	supported: function(){
		return !!window.openDatabase;
	},
	open: function(){	
		if (this.short_name == '') {
			wapp.log('ERROR: dbClass.short_name must be set before calling dbClass.open');
			return false;
		}
		if (this.version == '') {
			wapp.log('ERROR: dbClass.version must be set before calling dbClass.open');
			return false;
		}
		if (this.display_name == '') {
			wapp.log('ERROR: dbClass.display_name must be set before calling dbClass.open');
			return false;
		}		
		if (this.db != null) {
			wapp.log('dbClass.open: DB already open.');
			return true;
		}
		try {
		    if (!window.openDatabase) {
		    	wapp.log('dbClass.open: Error opening database. Not supported.');
		    	return false;
		    } else {
		        this.db = openDatabase(this.short_name, this.version, this.display_name, this.max_size);
		        wapp.log('dbClass.open: DB open.');
		        //this.transaction = this.db.transaction;
		        this.db.dropAll = this.dropAll;
		        this.db.dropTable = this.dropTable;
		        return true;
		    }
		} catch(e) {
		    if (e == 2) {
		    	wapp.log("dbClass.open: Invalid database version.");
		    } else {
		    	wapp.log("dbClass.open: Unknown error "+e+".");
		    }
		    return false;
		}
	},
	dropAll: function(){
		wapp.log("dbClass.dropAll: Dropping all tables from database " + this.short_name);
		this.db.transaction(
	        function (transaction) {
	        	var sql = "SELECT name FROM sqlite_master";
	        	transaction.executeSql(sql, [],
	        	function(transaction, results){
	        	    for (var i=0; i<results.rows.length; i++) {
	        	        var row = results.rows.item(i);
	        	        var tblName = row['name'];
	        	        if (tblName.indexOf('__') === -1 && tblName.indexOf('sqlite_autoindex') === -1){
		        	        this.dropTable(tblName);
	        	        }
	        	    }
	        	}.bind(this),
	        	function(transaction, error){
	        		wapp.log("dbClass.dropAll: SQL Error - " + error.message);
	        	});
	        }.bind(this)
	    );
	},
	dropTable: function(tblName){
		wapp.log("dbClass.dropTable: Dropping " + tblName + " from " + this.short_name);
		this.db.transaction(
	        function (transaction) {
	        	var sql = "DROP TABLE " + tblName;
	        	transaction.executeSql(sql, [],
	        	function(transaction, results){
	        		wapp.log("dbClass.dropTable: Table dropped.");
	        	}.bind(this), 
	        	function(transaction, error){
	        		wapp.log("dbClass.dropTable: Drop error - " + error.message);
	        	}.bind(this));
	        }.bind(this)
	    );
	},
	is_open: function(){
		if (this.db != null)
			return true;
		else
			return false;
	},
	close: function(){
		this.transaction = null;
		this.db = null;
	}
};
function Wapp(){}
Wapp.prototype = {
	user: {user_id: ''},
	wapp_menu: new MenuCtl(),
	apps_path: '/apps',
	app_name: '',		
    short_name: '',
    version: '',
    display_name: '',
    app_db: new dbClass(),
    wapp_db: new dbClass(),
    db: null,    
	status_icons: new status_icon_ctl(),
	online: false,
	cookie_prefix: '',
	cookie_path: '/',
	default_app: '/',
	dialog_id: '', // id of the active modal/dialog element if there is one
	_app_init: null,
	pages: [],
	pages_loaded: 0,
	supports: {
		localStorage: false,
		localDatabase: false,
		geoLocation: false
	},	
	restartApp: function(){
		window.location.href = this.apps_path + '/' + this.app_name;
	},
	loadApp: function(app_name){
		window.location.href = this.apps_path + '/' + app_name;
	},
	goHome: function(){
		window.location.href = '/';
	},		
	userInit: function(){
		if (this.user.user_id == 'Guest'){
			$('.login_item').show();
			$('.logout_item').hide();
			
		} else {
			$('.login_item').hide();
			$('.logout_item').show();
			
		}
		var domainName = document.domain;
		wapp.log("Domain: " + domainName);		
		$('#login_status_label').html(this.user.user_id + ' logged in.');
		$('.user_name_display').html(this.user.user_id);
		$('.user_name_domain_display').html(this.user.user_id + '@' + domainName);
		this.refreshAppMenu();
	},
	scrollToTop: function(){
		setTimeout(scrollTo, 0, 0, 1);
	},
	appName:function(){
		var tmp = window.location.pathname;
		var pos = tmp.indexOf(this.apps_path + '/');
		if (pos === 0) {
			// Seems to be an app
			tmp = tmp.substr(this.apps_path.length + 1);
			if (tmp.indexOf('/') != -1){
				tmp = tmp.substr(0,tmp.indexOf('/'));
			}
			return tmp;
		}
	},
	init:function(call_back){
		$.ajaxSetup({
			  cache:false
		});
		// AJAX events
		$('body').ajaxStart(function() {
			//$('#ajax_status').show();
			var src = $('#home_link img').attr('src_busy');
			$('#home_link img').attr('src',src);
			$('#home_link img').corner();
		});			
		$('body').ajaxStop(function() {
			//$('#ajax_status').hide();
			var src = $('#home_link img').attr('src_app');
			$('#home_link img').attr('src',src);
			$('#home_link img').corner();
		});	
		//
		this.app_name = this.appName();
		this.log('Loading ' + this.app_name);
		// Load wapp UI into the current app
		
		document.body.addEventListener("offline", function () {
			this.networkStatusUpdate(false);
		}.bind(this), false);
		document.body.addEventListener("online", function () {
			this.networkStatusUpdate(true);
		}.bind(this), false);		
		this.supports.localStorage = !!window.localStorage;
		this.supports.localDatabase = !!window.openDatabase;
		this.supports.geoLocation = !!navigator.geolocation;			
		if (this.short_name == '') {
			this.short_name = this.app_name;
		}
		if (this.version == '') {
			this.version = '0.01';
		}
		if (this.display_name == '') {
			this.display_name = this.app_name;
		}
		this.app_db.short_name = this.short_name;
		this.app_db.version = this.version;
		this.app_db.display_name = this.display_name;		
		this.wapp_db.short_name = "wapp";
		this.wapp_db.version = "0.1";
		this.wapp_db.display_name = "wapp";
		if (this.wapp_db.open()){
			this.db = this.wapp_db.db;
			this.verifyDB();
		}
		this.loadSettings();
		// Load any Wapp html element pages then call the callback 
		this._app_init = call_back;
		this.pages.push('/lib/wapp/wapp.php');
		this.loadPages();
	},
	hard_refresh: function(){
		
	},
	loadPages: function(){
		for (var i in this.pages){
			var page = this.pages[i];
			$.get(page, function(data) {
				$('body').append(data);
				this.pageLoaded();
			}.bind(this));

		}	
	},
	pageLoaded: function(){
		this.pages_loaded ++;
		if (this.pages_loaded == this.pages.length){
			// All pages loaded. Finish init
			this.uiInit();
			if (this._app_init)
				this._app_init();
		}
	},
	uiInit:function(){
		wapp.cache.init();
		// UI
		$('#home_link').click(function(){wapp.showDialog('#wapp_menu');});
		$('#page_shadow').click(function(){wapp.hideDialog();});		
		
		// Initialize tray icons
		//this.status_icons.add('menu_icon','','',true,function(){iui.showPageById('menu');});
		this.status_icons.add('net_status','','offline_icon',true,function(){wapp.status_icons.hide('net_status');});
		this.status_icons.add('db_status','','db_offline_icon',true,function(){iui.showPageById('info');});
		this.status_icons.add('geo_status','','geo_offline_icon');
		this.status_icons.add('mem_status','','mem_offline_icon');
		//this.status_icons.add('ajax_status','','',false);
		this.status_icons.add('msg_status','0');
		// Memory icon
		$('#mem_status > .status_icon').toggleClass('mem_offline_icon', !this.supports.localStorage);
		$('#mem_status > .status_icon').toggleClass('mem_online_icon', this.supports.localStorage);		
		// DB icon
		$('#db_status > .status_icon').toggleClass('db_offline_icon', !this.supports.localDatabase);
		$('#db_status > .status_icon').toggleClass('db_online_icon', this.supports.localDatabase);	
		// Geo location icon
		$('#geo_status > .status_icon').toggleClass('geo_offline_icon', !this.supports.geoLocation);
		$('#geo_status > .status_icon').toggleClass('geo_online_icon', this.supports.geoLocation);	
		// Enable date picker jQuery UI widget
		//$("#datepicker").datepicker();
		$("#login_form").submit(function(){this.login();}.bind(this));
		// Round the right side of the Home Button
		$('#home_link').corner('tr br');		
		
//		var domainName = document.domain;
//		wapp.log("Domain: " + domainName);
		
//		$('#wapp_menu_title').corner('tl tr');
//		$('#wapp_menu_title > div').corner();	
//		$('#wapp_menu').delegate('.menu_item','click',function(){
//			this.hideDialog('#wapp_menu');
//		}.bind(this));
		//this.refreshAppMenu();
		$('body').delegate('.detail_toggle','click touchend',function(event){
			this.log('touchit');
			var target = event.target;
			$(target).siblings('.more_detail').toggle();
		}.bind(this));
		
		// App Icon
		var app_icon = $('head link[rel="icon"]').attr('href');
		if (app_icon != '' && $('#home_link img').attr('src') != app_icon) {
			$('#home_link img').attr('src', app_icon);
			$('#home_link img').corner();
		}
		$('#home_link img').attr('src_app', app_icon);
		//$('#home_link img').css('visibility','visible');
		this.networkStatusUpdate(navigator.onLine);
		
		this.wappMenuInit();
		this.wappPanelInit();
		this.uiEventInit();
		this.pageShown();
	},
	uiEventInit: function(){
		// Setup any needed event catchers
		$('#loginName').keypress(function(e) {
		    if(e.keyCode == 13) {
		    	this.login();
		    }
		}.bind(this));		
		$('#loginPass').keypress(function(e) {
		    if(e.keyCode == 13) {
		    	this.login();
		    }
		}.bind(this));
		$('body').delegate('.wapp_dialog_close','click touchend',function(e) {
			wapp.log('close button clicked');
			this.hideDialog();
			//e.preventDefault();
			return false;
		}.bind(this));	
		$('body').delegate('[href="#restart"]','click',function(e) {
			wapp.log('Restart!');
			this.restartApp();
			return false;
		}.bind(this));	
		$('body').delegate('[href="#hard_reset"]','click',function(e) {
			wapp.log('Hard Reset!');
			this.hideDialog();
			this.cache.hard_reset();
			return false;
		}.bind(this));			
		$('body').delegate('[href="#filer"]','click',function(e) {
			wapp.log('App Load: Filer');
			this.loadApp('filer');
			return false;
		}.bind(this));			
		$('body').delegate('[href="#home"]','click',function(e) {
			wapp.log('Home');
			this.goHome();
			return false;
		}.bind(this));
		$('body').delegate('a','click',function(e){
			var page = e.currentTarget.hash;
			if ($(page).length > 0){
				if ($(page).hasClass('wapp_dialog')){
					wapp.showDialog(page);	
					return true;
				} else {
					stop = '';
				}
			}
		}.bind(this));
//		// AJAX events
//		$('body').ajaxStart(function() {
//			$('#ajax_status').show();
//		});			
//		$('body').ajaxStop(function() {
//			$('#ajax_status').hide();
//		});	
	},
	wappMenuInit: function(){
		this.wapp_menu = new MenuCtl();
		this.wapp_menu.addItem('#wapp_panel','Wapp Panel','/lib/images/settings.png');
		this.wapp_menu.addItem('#login','Login','/lib/images/keys.png');
		this.wapp_menu.addItem('#restart','Restart','/lib/images/restart_icon.jpg');
		this.wapp_menu.addItem('#hard_reset','Hard Reset','/lib/images/restart_icon.jpg');
		this.wapp_menu.addItem('#filer','Filer','/apps/filer/app_icon.png');
		this.wapp_menu.addItem('#home','Home','/lib/images/home.png');
		this.wapp_menu.addItem('#debug_div','Debug','/lib/images/debug.png');
		this.wapp_menu.iconView('#wapp_menu .wapp_dialog_main ');	
	},
	wappPanelInit: function(){
		var menu = new MenuCtl();
		menu.addItem('#wapp_account','Account');
		menu.addItem('#wapp_app_manager','Apps');		
		menu.addItem('#wapp_devices','Devices');
		menu.addItem('#wapp_groups','Groups');
		menu.addItem('#settings','Settings','/lib/images/settings.png');
		menu.addItem('#wapp_shares','Shares');		
		menu.addItem('#wapp_display','Themes');
		menu.addItem('#wapp_users','Users');
		menu.addItem('#wapp_services','Wapp Services');
		menu.iconView('#wapp_panel .wapp_dialog_main');	
	},
	refreshAppMenu: function(){
		return false;
		// DEBUG!
		for (var i in this.user.app_list){
			var app = this.user.app_list[i];
			this.wapp_menu.addItem(this.apps_path + '/' + app.name,app.title,app.icon);
		}			
		this.wapp_menu.iconView('#wapp_menu_main');
	},
	pingWapi: function() {
		this.wapiCom('ping','',true,function(resp){
			if (resp.data.user_id != this.user.user_id){
				this.user = resp.data;
				this.userInit();
			}
			this.pingApp();
			this.log(resp.text);
		}.bind(this));
		return null;
	},		
	pingApp: function() {
		this.appCom('','',true,function(resp){
//			if (resp.data.user_id != this.user.user_id){
//				this.user = resp.data;
//				this.userInit();
//			}
			this.log(resp.text);
		}.bind(this));
		return null;
	},	
	login: function(as_guest){
		if (as_guest){
			var user = 'Guest';
			var pass = 'guest';
		} else {
			var user = $('#loginName').val();
			var pass = $('#loginPass').val();
		}
		$('#loginPass').val('');
		var req = {user:user,pass:pass};
		this.wapiCom('login',req,true,function(resp){
			this.user = resp.data;
			if (resp.error != '') {
				$('#login_status_label').html(resp.error);
				return false;
			} else {
				this.userInit();
				return true;
			}
		}.bind(this));
	},
	logout: function(){
		this.login(true);
	},
	getSlashdot: function(){
		var url = 'http://slashdot.org';
		var pq = '';
		this.getPage(url,pq,function(resp){
			if (resp.data) {
				$('#debug_div').html('');
				tmp = resp.text;
				$('[data-fhtype="story"]', resp.text).each(function(){
					var sid = $(this).attr('data-fhid');
					var title = $('#title-' + sid, this).text();
					var story = $('#text-' + sid, this).text();
					var section = $('a', this)[0].innerHTML;
					var cmnt_cnt = 0;
					var html = $('#debug_div').html();
					html += title + "<br/><br/>" + story + "<br/><br/><br/>";
					$('#debug_div').html(html);
				});
			} else {
				wapp.log('getHTTPPage ERROR');
			}
			return true;
		}.bind(this));
		return true;
	},
	getPage: function(url,pq,cb){
		var req = {url:url,pq:pq};
		this.wapiCom('get_http',req,false,cb);
		return true;
	},
	wapiCom: function(com, data, min, succ_callback){
		this.com('wapi',com, data, min, succ_callback);
	},
	appCom: function(com, data, min, succ_callback){
		this.com(this.app_name, com, data, min, succ_callback);
	},
	com:function(app, com, data, min, succ_callback){
		if (typeof app == "undefined" || app == null) {
			var app = "";
		}		
		if (typeof com == "undefined" || com == null) {
			var com = "";
		}
		if (typeof data == "undefined" || data == null) {
			var data = "";
		}
		if (typeof min == "undefined" || min == null) {
			var min = false;
		}		
		// Send App Command Request
		var req = {
			app: app,
			com: com,
			data: data,
			min: min
		};
		//  JSON encode data
		var out_data = JSON.stringify(req);
		out_data = 'data=' + encodeURIComponent(out_data) + '';
		$.post('/lib/wapi/wapi.php',out_data,function(data){
			var obj = null;
			try {
				obj = JSON.parse(data);
			} catch(err) {
				//var txt = $('',data).text();
				var txt = data;
				wapp.log('WAPI ERROR: ' + txt);
			}			
			if (obj) {
				if (obj.cmds) {
				    for( var i = 0, len = obj.cmds.length; i < len; i++ ) {
				        var js = obj.cmds[i];
				        if (js != '') {
				        	eval(js);
				        }
				    }		
				}
				obj.cmds = null;
				if (succ_callback != null)
					succ_callback(obj);
			}
		}.bind(this));			
	},
	// Cookie code
	setCookie: function(name,value,days) {
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		if (this.cookie_prefix != '') {
			name = this.cookie_prefix + '_' + name;
		}
		document.cookie = name+"="+value+expires+";";
	},
	getCookie: function(name) {
		if (this.cookie_prefix != '') {
			name = this.cookie_prefix + '_' + name;
		}		
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	},
	delCookie: function(name) {
		createCookie(name,"",-1);
	},
	// End Cookie End 	
	networkStatusUpdate: function(online) {
		this.online = online;
		$('#net_status > .status_icon').toggleClass('offline_icon', !online);
		$('#net_status > .status_icon').toggleClass('online_icon', online);	
		if (online) {			
			this.pingWapi();
		}
	},
	log: function(msg){
		console.log(msg);
		if ($('#wapp_info > [data-role="content"]').length > 0)
			$('#wapp_info > [data-role="content"]')[0].innerHTML += msg + "<br />";
		if ($('#debug').length > 0)
			$('#debug')[0].innerHTML += msg + "<br />";
	},
	frmSubmit: function(e){
		var target = e.target;
		var tmp = $(target).closest('form');
		if (tmp.length != 1) {
			wapp.log('Invalid form submitted.');
			return false;
		}
		var frm = tmp[0];
		if (frm.id == 'login_form'){
			wapp.log(frm.id + ' form submitted.');
		} else {
			wapp.log(frm.id + ' wasn\'t handled.');
		}
		history.back();
		return false;
	},	
	defaultSettings: function(){
		this.settings = {
			user_name: '',
			password_hash: '',
			password_salt: '',
			
			nick_name: '',
			home_base: {},
			enable_geolocation: true,
			google_maps_has_sensor: false
		};
	},	
	loadSettings: function() {
		if (window.localStorage) {
			// Check for saved settings in localStorage
			if (localStorage.wapp_settings) {
				var strSettings = localStorage.wapp_settings;
				this.settings = JSON.parse(strSettings);
			} else {
				this.defaultSettings();
			}
			// Display settings
			// $('#home_base')[0].value = this.settings.home_base;
			// $('#enable_geolocation')[0].setAttribute("toggled", this.settings.enable_geolocation);
		} else {
			alert('localStorage not supported.');
		}
	},
	saveSettings: function() {
		// Read settings from inputs
		// this.settings.home_base = $('#home_base')[0].value;
		// this.settings.enable_geolocation = $('#enable_geolocation')[0].getAttribute("toggled");
		// Now save the settings
		var strSettings = JSON.stringify(this.settings);
		localStorage.wapp_settings = strSettings;	
	},
	resetSettings: function(){
		localStorage.removeItem('wapp_settings');
		this.loadSettings();
	},
	loadJSFile: function(filename){
		var fileref = document.createElement('script');
		fileref.setAttribute("type","text/javascript");
		fileref.setAttribute("src", filename);
	},
	loadCSSFile: function(filename){
		var fileref = document.createElement("link");
		fileref.setAttribute("rel", "stylesheet");
		fileref.setAttribute("type", "text/css");
		fileref.setAttribute("href", filename);
	},
	loadDependencies: function(){
		
	},
	// Wapp DB
	verifyDB: function(){
		this.log("wapp.verifyDB()");
		this.db.transaction(
	        function (transaction) {
	        	tblName = 'Users'; // Test for 1 of the tables that should exist
	        	sql = "SELECT name FROM sqlite_master WHERE name='" + tblName + "'";
	        	transaction.executeSql(sql, [],
	        	function(transaction, results){
	        		if (results.rows.length != 1) {
	        			this.log("wapp.verifyDB: Creating tables.");
	        			this.createTables();
	        		} else {
	        			this.log("wapp.verifyDB: Tables exist.");
	        		}
	        	}.bind(this), 
	        	function(transaction, error){
	        		this.log("wapp.verifyDB: SQL Error - " + error.message);
	        	}.bind(this));
	        }.bind(this)
	    );		
	},
	createTables: function(){
		this.db.transaction(
	        function (transaction) {
	        	transaction.executeSql('CREATE TABLE IF NOT EXISTS Users(id INTEGER NOT NULL PRIMARY KEY, UserName TEXT UNIQUE NOT NULL, PasswordSalt TEXT, PasswordHash TEXT);');
	        }
	    );
		this.populateTables();
	},
	populateTables: function(){
		this.db.transaction(
		    function (transaction) {
				transaction.executeSql("INSERT INTO Users(UserName) VALUES (?)", ['Guest']);
		    }
		);
	},
	// iUI related function
	pageShown: function(){
		$('body').css('background-color',$('body > [selected="true"]').css('background-color'));
		$('body').css('background-image',$('body > [selected="true"]').css('background-image'));
		var ph = $("#header");
		if (ph.length == 1 && $('body > [selected="true"]').length == 1){
			var pageHeader = ph[0];
			var page = $('body > [selected="true"]')[0];
			if (page.getAttribute("no-header")){
				//pageHeader.style.display = "none";
				$("#header").slideUp();
			} else {
				//pageHeader.style.display = "block";
				$("#header").slideDown();
			}	
		}
		wapp.scrollToTop();
	},
	showDialog: function(dialog_selector){		
		if ($(dialog_selector).length == 0) {
			return false;
		}
		this.hideDialog();	
		if ($(dialog_selector + ' .wapp_dialog_head').length == 0) {
			$(dialog_selector).prepend('<div class="wapp_dialog_head"><div class="wapp_dialog_title"></div><div class="wapp_dialog_close"></div></div>');
			var title = $(dialog_selector).attr('title');
			$(dialog_selector + ' .wapp_dialog_title').html(title);
		}		
		var ht = $(document).height();
		ht = ht - 30;
		$('#page_shadow').css('height',ht + 'px');
		$('#page_shadow').css('display','block');
		
		$(dialog_selector).corner();
		$('.wapp_dialog_head').corner('tl tr');
		$('.wapp_dialog_title').corner();
		$('.wapp_dialog_close').corner();
		
		$(dialog_selector).css('display','block');		
		$(dialog_selector).attr('wapp-dialog','true');
		wapp.log("showDialog("+dialog_selector+")");
		wapp.scrollToTop();
		return true;
	},	
	showDialog_OLD: function(dialog_selector){		
		if ($(dialog_selector).length == 0) {
			return false;
		}
		this.hideDialog();
		$(dialog_selector).corner();
		var ht = $(document).height();
		$('#page_shadow').css('height',ht + 'px');
		$('#page_shadow').css('display','block');
		$(dialog_selector).css('display','block');		
		$(dialog_selector).attr('wapp-dialog','true');
		wapp.log("showDialog("+dialog_selector+")");
		return true;
	},
	hideDialog: function(dialog_selector){
		// Exit function if the dialog isn't open
		if (!dialog_selector)
			var dialog_selector = '[wapp-dialog="true"]';
		if ($(dialog_selector).length == 0) {
			return false;
		}
		if ($(dialog_selector).attr('wapp-dialog') != 'true') {
			return false;
		}
		wapp.log("hideDialog()");
		$('#page_shadow').css('display','none');
		$(dialog_selector).css('display','none');
		$(dialog_selector).attr('wapp-dialog','false');
		return true;
	}	
};

Wapp.prototype.cache = {
	supported: false,
	last_file_cnt: 0,
	last_manifest_file: '',
	last_status: 'UNCACHED',
	last_update_time: '',
	update_time: '',
	file_cnt: 0,
	manifest_file: '',	
	status: 'UNCACHED',
	status_enum: ['UNCACHED','IDLE','CHECKING','DOWNLOADING','UPDATEREADY','OBSOLETE'],
	_file_cnt: 0,
	_clear: false,
	_manifest_size: 0,
	_manifest_path: '',
	_cache_total_size: 0,
	_cache_file_count: 0,
	_percent_downloaded: 0,
	_hard_clear: false,
	_hard_reset: false,
	net_only: true,
	busy: false,
	init: function() {
		this.controlLock(true);
		$( ".cache_update_progress" ).progressbar({ disabled: false });
		if (!window.applicationCache) {
			wapp.log('applicationCache not supported.');
			this.progressUpdate('Cache not supported');
			return false;
		}
		this.progressUpdate('Ready');
		this._load_data();
		this.supported = true;
		this.manifest_file = $('html').attr('manifest');
		if (this.manifest_file == this.last_manifest_file) {
			this.update_time = this.last_update_time;
			this.file_cnt = this.last_file_cnt;
			if (this.last_status == 'UPDATED') {
				this.status = 'CACHED';
			} else {
				this.status = this.last_status;
			}
		}
		this._save_data();
		var cache = window.applicationCache;
		cache.addEventListener('updateready', function(){this.cacheEventListener(event);}.bind(this), false);
		cache.addEventListener('noupdate', function(){this.cacheEventListener(event);}.bind(this), false);
		cache.addEventListener('progress', function(){this.cacheEventListener(event);}.bind(this), false);
		cache.addEventListener('downloading', function(){this.cacheEventListener(event);}.bind(this), false);
		cache.addEventListener('checking', function(){this.cacheEventListener(event);}.bind(this), false);
		cache.addEventListener('obsolete', function(){this.cacheEventListener(event);}.bind(this), false);
		cache.addEventListener('cached', function(){this.cacheEventListener(event);}.bind(this), false);
		cache.addEventListener('error', function(){this.cacheEventListener(event);}.bind(this), false);
		this.loadManifestCookies();
		this.controlLock(false);
		return true;
	},
	showCacheInfo: function(){
		if (!this._cache_total_size){
			this._cache_total_size = 0;
		}
		if (!this._cache_file_count) {
			this._cache_file_count = 0;
		}
		$('#cache_total_size').html(convertBytes(this._cache_total_size));
		$('#cache_file_cnt').html(this._cache_file_count);
		$('#cache_last_update').html(this.update_time);
		if (this._cache_file_count <= 0) {
			$('#offline_cache_toggle').attr('toggled', 'false');
			this.net_only = true;
		} else {
			$('#offline_cache_toggle').attr('toggled', 'true');
			this.net_only = false;
		}
	},	
	loadManifestCookies: function(){
		this._manifest_size = wapp.getCookie('MANIFEST_SIZE');
		this._manifest_path = wapp.getCookie('MANIFEST_PATH');
		this._cache_total_size = wapp.getCookie('MANIFEST_CACHE_TOTAL_SIZE');
		this._cache_file_count = wapp.getCookie('MANIFEST_CACHE_FILE_COUNT');
		this.showCacheInfo();
	},
	cacheEventListener: function(event){
		switch(event.type){
			case 'checking':
				// Checking for update...
				wapp.log('CACHE_EVENT CHECKING');
				this.progressUpdate('Checking');	
				this.controlLock(true);
				break;	
			case 'downloading':
				this.loadManifestCookies();
				// Downloading started...
				// set initially to -1. the manifest file the first download and ignored
				this._file_cnt = -1;
				this._percent_downloaded = 0;
				this.progressUpdate();
				wapp.log('CACHE_EVENT DOWNLOADING');
				this.controlLock(true);
				break;		
			case 'progress':
				// A file has been downloaded...
				this._file_cnt ++;
				if (this._file_cnt >= 0 && this._cache_file_count >= 0) {
					if (this._cache_file_count > 0){
						var perc = (this._file_cnt / this._cache_file_count) * 100;
						if (perc >= 99) {
							perc = 99;
						}
						if (this._percent_downloaded >= 99) {
							if (this._percent_downloaded + 0.1 < 100) {
								this._percent_downloaded = this._percent_downloaded - 0 + 0.1;
							} else {
								this._percent_downloaded = 99.9;
							}
						} else {
							this._percent_downloaded = perc;
						}
						this.progressUpdate();
					}
				}
				this.controlLock(true);
				break;				
			case 'noupdate':
				// No update. Done.
				wapp.log('CACHE_EVENT NOUPDATE');
				this.progressUpdate('No Update');
				this.controlLock(false);
				break;
			case 'error':
				// Error. Done.
				this._clear = false;
				wapp.log('CACHE_EVENT ERROR');
				this.progressUpdate('Cache Error');
				break;					
			case 'cached':
				// App cached (First cache). Done.
				this.file_cnt = this._file_cnt;
				this._file_cnt = 0;
				this.status = 'CACHED';
				var now = new Date();
				this.update_time = now.format("m/dd/yy h:MM:ss TT");
				this._save_data();
				this._clear = false;
				this._percent_downloaded = 100;
				this.progressUpdate('App Cached');	
				this.controlLock(false);
				break;					
			case 'updateready':
				// Update complete. 
				// An app restart or a swapCache is needed to enable the update. Done.
				this.file_cnt = this._file_cnt;
				this._file_cnt = 0;
				this.status = 'UPDATED';
				var now = new Date();
				this.update_time = now.format("m/dd/yy h:MM:ss TT");
				this._save_data();
				this._clear = false;
				this._percent_downloaded = 100;
				if (this.file_cnt = 0) {
					this.progressUpdate('Cache Deleted');
				} else {
					this.progressUpdate('Cache Updated');
				}
				this.controlLock(false);
				break;		
			case 'obsolete':
				// Manifest file no longer available on the server
				// Cache was deleted. Done.
				this.status = 'OBSOLETE';
				var now = new Date();
				this.update_time = now.format("m/dd/yy h:MM:ss TT");
				this.file_cnt = 0;
				this._save_data();
				if (this._clear){
					this._clear = false;
					this.update();
					return;
				}
				this.progressUpdate('Cache Deleted');
				this.controlLock(false);
				break;				
			default:
				wapp.log('CACHE_EVENT unhandled event.type');
		}		
	},
	progressUpdate: function(msg){
		if (!msg) var msg = '';
		if (msg == ''){
			$('.cache_update_progress > .ui-progressbar-value').html(this._percent_downloaded.toFixed(1) + '%');
			var val = this._percent_downloaded.toFixed(0) - 0;
			$( ".cache_update_progress" ).progressbar( "option", "value", val );	
		} else {
			$('.cache_update_progress > .ui-progressbar-value').html(msg);
			$( ".cache_update_progress" ).progressbar( "option", "value", 100 );
			this.loadManifestCookies();			
		}
	},
	_save_data: function(){
		if (window.localStorage){
			localStorage.wapp_cache_file_cnt = this.file_cnt;
			localStorage.wapp_cache_manifest_file = this.manifest_file;
			localStorage.wapp_cache_status = this.status;
			localStorage.wapp_cache_update_time = this.update_time;
			wapp.log('Manifest File: ' + this.manifest_file);
			wapp.log('Files Cached: ' + this.file_cnt);
			wapp.log('Cache Status: ' + this.status);
			wapp.log('Updated : ' + this.update_time);
			return true;
		}
		return false;		
	},
	_load_data: function(){
		if (window.localStorage){
			this.last_file_cnt = localStorage.wapp_cache_file_cnt;
			this.last_manifest_file = localStorage.wapp_cache_manifest_file;
			this.last_status = localStorage.wapp_cache_status;
			this.last_update_time = localStorage.wapp_cache_update_time;
			wapp.log('Last Manifest File: ' + this.last_manifest_file);
			wapp.log('Last Files Cached: ' + this.last_file_cnt);
			wapp.log('Last Cache Status: ' + this.last_status);
			wapp.log('Last Updated : ' + this.last_update_time);
			return true;
		}
		return false;
	},
	update: function(){
		wapp.log('cache.update');
		try{
			window.applicationCache.update();
		}catch(err){
			this.progressUpdate('Cache Error');
			wapp.log('window.applicationCache state error.');
		}		
	},
	swap: function(){
		wapp.log('cache.swap');		
		var cache = window.applicationCache;
		if (cache.status == 4)
			cache.swapCache();
		else
			wapp.log('cache update not ready');
	},
	clear: function(){
		this._clear = true;
		wapp.log('cache.clear');
		wapp.setCookie('MANIFEST','clear',1);
		this.update();
	},
	hard_reset: function(){
		this._hard_clear = true;
		this._hard_reset = true;
		this.refresh();
	},
	netOnly: function(){
		wapp.log('cache.netOnly');
		wapp.setCookie('MANIFEST','net',1);
		this.update();
	},
	refresh: function(){
		wapp.log('cache.refresh');
		wapp.setCookie('MANIFEST','refresh',1);
		this.update();
	},
	toggle: function(){
		if (this.busy)
			return false;
		this.controlLock(true);
		if (this.net_only) {
			this.refresh();
		} else {
			this.netOnly();
		}
	},
	controlLock: function(locked){
		if (this.busy == locked)
			return;
		wapp.log('Cache Control Lock: ' + locked);
		this.busy = locked;
		if (locked) {
			$('#offline_cache_toggle').attr('disabled','true');
		} else {
			if (this._hard_clear) {
				this._hard_clear = false;
				this.netOnly();
			} else if (this._hard_reset) {
				wapp.restartApp();
			} else {
				$('#offline_cache_toggle').removeAttr('disabled');
			}
		}
	}
};

var wapp = new Wapp();

function onKeyPressNumbersOnly(event) {
	var test = document.forms['order_entry_form'].customer_phone_lookup.value;
	var strError = '';
	var ret = validateInput('maxlen=3',document.forms['order_entry_form'].customer_phone_lookup,strError);
	return true;
}

function disableSelection(target){
	if (typeof target.onselectstart!="undefined") //IE route
		target.onselectstart=function(){return false;};
	else if (typeof target.style.MozUserSelect!="undefined") //Firefox route
		target.style.MozUserSelect="none";
	else //All other route (ie: Opera)
		target.onmousedown=function(){return false;};
		target.style.cursor = "default";
}
function convertBytes(bytes) {
	if ((bytes - 0) < 1024){
		return bytes + " bytes";
	}
	var s = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
	var e = Math.floor(Math.log(bytes + 1)/Math.log(1024));
	return (bytes/Math.pow(1024, Math.floor(e))).toFixed(2)+" "+s[e];
}

function Asc(String){return String.charCodeAt(0);}
function Chr(AsciiNum){return String.fromCharCode(AsciiNum);}


// Date Formatter
/*
 * Date Format 1.2.3
 * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
 * MIT license
 *
 * Includes enhancements by Scott Trenda <scott.trenda.net>
 * and Kris Kowal <cixar.com/~kris.kowal/>
 *
 * Accepts a date, a mask, or a date and a mask.
 * Returns a formatted version of the given date.
 * The date defaults to the current date/time.
 * The mask defaults to dateFormat.masks.default.
 */

var dateFormat = function () {
	var	token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
		timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
		timezoneClip = /[^-+\dA-Z]/g,
		pad = function (val, len) {
			val = String(val);
			len = len || 2;
			while (val.length < len) val = "0" + val;
			return val;
		};

	// Regexes and supporting functions are cached through closure
	return function (date, mask, utc) {
		var dF = dateFormat;

		// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
		if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
			mask = date;
			date = undefined;
		}

		// Passing date through Date applies Date.parse, if necessary
		date = date ? new Date(date) : new Date;
		if (isNaN(date)) throw SyntaxError("invalid date");

		mask = String(dF.masks[mask] || mask || dF.masks["default"]);

		// Allow setting the utc argument via the mask
		if (mask.slice(0, 4) == "UTC:") {
			mask = mask.slice(4);
			utc = true;
		}

		var	_ = utc ? "getUTC" : "get",
			d = date[_ + "Date"](),
			D = date[_ + "Day"](),
			m = date[_ + "Month"](),
			y = date[_ + "FullYear"](),
			H = date[_ + "Hours"](),
			M = date[_ + "Minutes"](),
			s = date[_ + "Seconds"](),
			L = date[_ + "Milliseconds"](),
			o = utc ? 0 : date.getTimezoneOffset(),
			flags = {
				d:    d,
				dd:   pad(d),
				ddd:  dF.i18n.dayNames[D],
				dddd: dF.i18n.dayNames[D + 7],
				m:    m + 1,
				mm:   pad(m + 1),
				mmm:  dF.i18n.monthNames[m],
				mmmm: dF.i18n.monthNames[m + 12],
				yy:   String(y).slice(2),
				yyyy: y,
				h:    H % 12 || 12,
				hh:   pad(H % 12 || 12),
				H:    H,
				HH:   pad(H),
				M:    M,
				MM:   pad(M),
				s:    s,
				ss:   pad(s),
				l:    pad(L, 3),
				L:    pad(L > 99 ? Math.round(L / 10) : L),
				t:    H < 12 ? "a"  : "p",
				tt:   H < 12 ? "am" : "pm",
				T:    H < 12 ? "A"  : "P",
				TT:   H < 12 ? "AM" : "PM",
				Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
				o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
				S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
			};

		return mask.replace(token, function ($0) {
			return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
		});
	};
}();

// Some common format strings
dateFormat.masks = {
	"default":      "ddd mmm dd yyyy HH:MM:ss",
	shortDate:      "m/d/yy",
	mediumDate:     "mmm d, yyyy",
	longDate:       "mmmm d, yyyy",
	fullDate:       "dddd, mmmm d, yyyy",
	shortTime:      "h:MM TT",
	mediumTime:     "h:MM:ss TT",
	longTime:       "h:MM:ss TT Z",
	isoDate:        "yyyy-mm-dd",
	isoTime:        "HH:MM:ss",
	isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
	isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};

// Internationalization strings
dateFormat.i18n = {
	dayNames: [
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
	],
	monthNames: [
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
		"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
	]
};

// For convenience...
Date.prototype.format = function (mask, utc) {
	return dateFormat(this, mask, utc);
};
wapp.init(function(){
	alert('Done!');
});